#include "AtencionUsuario.h"
#include <sstream>
AtencionUsuario::AtencionUsuario(int fd, int fd_av, std::ifstream* arch_usuarios,
		std::map<unsigned int, Clase*> *clases,
		std::map<unsigned int, Curso*> *cursos, Mutex *mutexClase, Mutex *mutexCurso) {
	this->socket = new Socket(fd);
	this->socket_av = new Socket(fd_av);
	this->atendiendo = true;
	this->arch_usuarios = arch_usuarios;
	this->clases = clases;
	this->cursos = cursos;
	codClase_a_iniciar = 0;
	codCurso_a_iniciar = 0;
	this->mutexClase = mutexClase;
	this->mutexCurso = mutexCurso;
	nombre_usuario = "";
	clave = "";
}

bool AtencionUsuario::getAtendiendo() {
	return this->atendiendo;
}

bool AtencionUsuario::recibirInstrucciones(char **argumentos, char *cod_op,	bool profe, bool alumno,
		unsigned int *tam_arg) {
	char buffer[TAM_BLOQUE_INI];
	int tam_bloque_ini = sizeof(int) + sizeof(char);
	*argumentos = NULL;
	if(!this->socket->recibir(buffer, tam_bloque_ini)) {
		std::cerr << "No se pudo recibir el codigo y el tamanio" << std::endl;
		return false;
	}
	(*cod_op) = buffer[0];
	if(((*cod_op) != '0') && ((*cod_op) != '1') && (!profe) && (!alumno)) {
		std::cerr << "Codigo de Operacion Invalido" << std::endl;
		return false;
	}
	(*tam_arg) = *(unsigned int*)&buffer[1];
	memmove(buffer, &buffer[4], TAM_BLOQUE_INI - 4);
	(*argumentos) = new char[(*tam_arg)];
	if((*tam_arg) >= 0) {
		if(!this->socket->recibir((*argumentos), (*tam_arg))) {
			std::cout << "No se pudo recibir " << std::endl;
			delete[] (*argumentos);
			return false;
		}
	}
	return true;
}

void AtencionUsuario::run() {
	unsigned int tam_arg;
	std::string codClase, codCurso;
	char cod_op;
	bool profesor = false, alumno = false;
	char *argumentos = NULL;
	std::ofstream* arch_adj;
	std::string nom_arch;
	unsigned long tam_arch = 0, aux = 0;
	while(1) {

		if(!this->recibirInstrucciones(&argumentos, &cod_op, profesor, alumno, &tam_arg))
			break;

		this->login(&profesor, &alumno, cod_op, &argumentos);

		if(profesor) {
			switch(cod_op) {
			// Inicia una clase
			case('A'): {
				std::cout << "Recibi para iniciar la clase" << std::endl;
				this->separar_Argumentos(&argumentos, &codClase_a_iniciar, &codCurso_a_iniciar, tam_arg);
				if(!this->iniciarClase()) {
					codClase_a_iniciar = 0;
					codCurso_a_iniciar = 0;
				}
				break;
			}
			// Pausa una clase, la unica q tiene q estar dandose
			case('B'): {
				std::cout << "Recibi para pausar una clase" << std::endl;
				this->pausarClase();
				break;
			}
			// Crea una clase
			case('C'): {
                std::string descripcion;
                char codDisponibilidad;
                codClase = argumentos;
                descripcion = &argumentos[codClase.size() + 1];
                codDisponibilidad = argumentos[codClase.size() + descripcion.size() + 2];
                this->CrearClase(this->String_toInt(codClase), descripcion, codDisponibilidad);
				break;
			}
			// Crea un curso
			case('D'): {
				codCurso = argumentos;
				std::string descripcion;
				descripcion = &argumentos[codCurso.size() + 1];
				this->CrearCurso(this->String_toInt(codCurso), descripcion);
				break;
			}
			// Se añade una clase a un curso
			case('E'): {
				codCurso = argumentos;
				codClase = &argumentos[codCurso.size() + 1];
				char codDisponibilidad;
				std::string descripcion = &argumentos[codCurso.size() + codClase.size() + 2];
				codDisponibilidad = argumentos[codClase.size() + codCurso.size() + descripcion.size() + 3];
				this->AnadirClase(this->String_toInt(codCurso), this->String_toInt(codClase), descripcion, codDisponibilidad);
				break;
			}
			// Llego un mensaje de chat
			case('F'): {
				/*std::cout << "Llego el mensaje: " << argumentos << std::endl;
        		if (codCurso_a_iniciar > 0) {
          	  		Clase* clase = (*cursos)[codCurso_a_iniciar]->getClase(codClase_a_iniciar);
          	  	  	clase->enviar_mensaje_al_chat(nombre_usuario, argumentos);
        		} else {
          	  	(*clases)[codClase_a_iniciar]->enviar_mensaje_al_chat(nombre_usuario, argumentos);
        		}*/
				break;
			}
			// Continuar clase pausada
			case('G'): {
				this->continuarClase();
				break;
			}
			// Terminar clase iniciada
			case('H'): {
				this->terminarClase();
				break;
			}
			case('I'): {   // Recibio el nombre y el tamanio del archivo
				nom_arch = recibirNomArch(argumentos, tam_arg, tam_arch);
				arch_adj = new std::ofstream(nom_arch.c_str());
				break;
			}
			case('J'): {   // Recibio datos del archivo
				arch_adj->write(argumentos, tam_arg);
				aux += tam_arg;
				if(aux == tam_arch) {
					arch_adj->close();
					delete arch_adj;
				}
				break;
			}
			}
		}
		if(alumno) {
			switch(cod_op) {
			// Inscribirse a una clase
			case('A'): {
				this->inscripcionClase(&argumentos);
				break;
			}
			// Inscribirse a un curso
			case('B'): {
				this->inscripcionCurso(&argumentos);
				break;
			}
			// Enviar la cartelera de cursos
			case('C'): {
				this->enviarCartelera();
				break;
			}
			// Enviar los videos disponibles
			case('D'): {
				this->enviarVideosDisponibles();
				break;
			}
			// Envia el video
			case('E'): {
				std::string codigo = argumentos;
				this->enviarVideo(codigo);
				break;
			}
			//Llego un mensaje de chat
			case('F'): {
				/*(*clases)[codClase_a_iniciar]->enviar_mensaje_al_chat(nombre_usuario, argumentos);
				if (codCurso_a_iniciar > 0) {
			    Clase* clase = (*cursos)[codCurso_a_iniciar]->getClase(codClase_a_iniciar);
			    clase->enviar_mensaje_al_chat(nombre_usuario, argumentos);
				} else {
				  (*clases)[codClase_a_iniciar]->enviar_mensaje_al_chat(nombre_usuario, argumentos);
				}*/
				break;
			}
			// El alumno entro en clase
			case('G'): {
			  // Carga los datos de la clase a la que entro el alumno en
			  // codClase_a_iniciar y codCurso_a_iniciar
			  this->separar_Argumentos(&argumentos, &codClase_a_iniciar, &codCurso_a_iniciar, tam_arg);
			  break;
			}
			// El alumno salio de clase
			case('H'): {
			  this->codClase_a_iniciar = 0;
			  this->codCurso_a_iniciar = 0;
			  break;
			}
			case('I'): {
			  this->enviarAdjuntosDisponibles();
			  break;
			}
			case('J'): {
				std::string nom_arch = argumentos;
			  	this->enviarAdjunto(nom_arch);
			  	break;
			}
		}
		}
		delete[] argumentos;
	}
	this->cerrarClase();
	this->matarSockets();
	this->pararAtencion();
}

int AtencionUsuario::enviarVideo(std::string codigo) {
	unsigned long tam_video = 0, tam_envio = 0;
	char cod_op = 'A';
	std::string codCurso;
	std::string codClase;
	std::string descrip;
	std::string nom_arch;
	std::ifstream video;
	int aux = codigo.find('_');
	std::cout << "codigo " << codigo << std::endl;
	if(aux == -1) {
		codClase = codigo;
		Clase *clase = clases->find(this->String_toInt(codClase))->second;
		std::cout << "clase->getDescripcion() " << clases->find(this->String_toInt(codClase))->second->getCodigo() << std::endl;
		descrip = clase->getDescripcion();
	}
	else {
		codCurso = codigo.substr(0, aux);
		codClase = codigo.substr(aux + 1, codigo.size());
		Curso *curso = cursos->find(this->String_toInt(codCurso))->second;
		Clase *clase = curso->getClase(this->String_toInt(codClase));
		descrip = clase->getDescripcion();
	}
	codigo += '_';
	codigo += descrip;
	nom_arch = codigo;
	nom_arch += ".avi";
	video.open(nom_arch.c_str(), std::ios_base::binary);
	video.seekg(0, std::ios::end);
	tam_video = video.tellg();
	video.seekg(0, std::ios::beg);

	// Mando el video
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_video, sizeof(tam_video))) {
		std::cerr << "No se pudo enviar el tamaño" << std::endl;
		return -1;
	}
	const int tam_paquete = 1024;
	tam_envio = tam_paquete;
	char paquete[tam_paquete];
	while(tam_video > 0) {
		if(tam_video < tam_envio)
			tam_envio = tam_video;
		video.read(paquete, tam_envio);
		if(!socket->enviar(paquete, tam_envio)) {
			std::cerr << "Error enviando archivo" << std::endl;
			return -1;
		}
		tam_video -= tam_envio;
	}
	return 0;
}

int AtencionUsuario::enviarVideosDisponibles() {
	// Primero me fijo las clases individualess
	if(this->enviarVideosDispClasesIndiv() == -1) {
		std::cerr << "Problemas de conectividad" << std::endl;
		return -1;
	}

	// Me fijo en los cursos q clases finalizaron
	if(this->enviarVideosDispCursos() == -1) {
		std::cerr << "Problemas de conectividad" << std::endl;
		return -1;
	}

	// No envia mas entonces envio un cod de op distinto
	char cod_op = 'F';
	int tam_arg = 0;
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	return 0;
}

int AtencionUsuario::enviarVideosDispClasesIndiv() {
	std::list<unsigned int>::iterator it;
	char cod = 'A';
	std::string codClase;
	int tam_arg = 0;
	mutexClase->lock();
	for(it = inscripcionesClases.begin(); it != inscripcionesClases.end(); ++it) {
		Clase *clase = clases->find(*it)->second;
		if((clase->esta_Terminada()) && (clase->getDisponibilidad())) {
			codClase = this->Int_toString(clase->getCodigo());
			if(this->enviarCodDescrp(cod, tam_arg, codClase, "") == -1)
				return -1;
		}
	}
	mutexClase->unlock();
	return 0;
}

int AtencionUsuario::enviarVideosDispCursos() {
	std::list<unsigned int>::iterator it_cursos;
	std::map<unsigned int, Clase*> clasesCurso;
	std::map<unsigned int, Clase*>::iterator it_clases;
	char cod = 'B';
	std::string codClase, codCurso;
	int tam_arg = 0;
	mutexCurso->lock();
	for(it_cursos = inscripcionesCursos.begin(); it_cursos != inscripcionesCursos.end(); it_cursos++) {
		clasesCurso = cursos->find(*it_cursos)->second->getClases();
		codCurso = this->Int_toString((*it_cursos));
		for(it_clases = clasesCurso.begin(); it_clases != clasesCurso.end(); it_clases++) {
			if(((*it_clases).second->esta_Terminada()) && ((*it_clases).second->getDisponibilidad())) {
				codClase = this->Int_toString((*it_clases).second->getCodigo());
				if(this->enviarCodDescrp(cod, tam_arg, codClase, codCurso) == -1)
					return -1;
			}
		}
	}
	mutexCurso->unlock();
	return 0;
}

int AtencionUsuario::enviarAdjunto(std::string nom_arch) {
	unsigned long tam_adjunto = 0, tam_envio = 0;
	char cod_op = 'A';
	std::ifstream adjunto;
	nom_arch.assign(nom_arch, nom_arch.find_last_of('/') + 1, nom_arch.size());
	adjunto.open(nom_arch.c_str(), std::ios_base::binary);
	adjunto.seekg(0, std::ios::end);
	tam_adjunto = adjunto.tellg();
	adjunto.seekg(0, std::ios::beg);

	// Mando el video
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_adjunto, sizeof(tam_adjunto))) {
		std::cerr << "No se pudo enviar el tamaño" << std::endl;
		return -1;
	}
	const int tam_paquete = 1024;
	tam_envio = tam_paquete;
	char paquete[tam_paquete];
	while(tam_adjunto > 0) {
		if(tam_adjunto < tam_envio)
			tam_envio = tam_adjunto;
		adjunto.read(paquete, tam_envio);
		if(!socket->enviar(paquete, tam_envio)) {
			std::cerr << "Error enviando archivo" << std::endl;
			return -1;
		}
		tam_adjunto -= tam_envio;
	}
	return 0;
}

int AtencionUsuario::enviarAdjuntosDisponibles() {
	// Primero me fijo las clases individualess
	if(this->enviarAdjuntosDispClasesIndiv() == -1) {
		std::cerr << "Problemas de conectividad" << std::endl;
		return -1;
	}

	// Me fijo en los cursos q clases finalizaron
	if(this->enviarAdjuntosDispCursos() == -1) {
		std::cerr << "Problemas de conectividad" << std::endl;
		return -1;
	}

	// No envia mas entonces envio un cod de op distinto
	char cod_op = 'F';
	int tam_arg = 0;
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	return 0;
}

int AtencionUsuario::enviarAdjuntosDispClasesIndiv() {
	std::list<unsigned int>::iterator it;
	char cod = 'A';
	std::string codClase;
	mutexClase->lock();
	for(it = inscripcionesClases.begin(); it != inscripcionesClases.end(); ++it) {
		Clase *clase = clases->find(*it)->second;
		codClase = this->Int_toString(clase->getCodigo());
		std::list<std::string>::iterator iter;
		for(iter = clase->getAdjuntos()->begin();iter != clase->getAdjuntos()->end();++iter) {
			std::string nom_adj = *iter;
			if(this->enviarCodDescrp(cod, 0, codClase, nom_adj) == -1)
				return -1;
		}
	}
	mutexClase->unlock();
	return 0;
}

int AtencionUsuario::enviarAdjuntosDispCursos() {
	std::list<unsigned int>::iterator it_cursos;
	std::map<unsigned int, Clase*> clasesCurso;
	std::map<unsigned int, Clase*>::iterator it_clases;
	char cod = 'B';
	std::string codClase, codCurso;
	int tam_arg = 0;
	mutexCurso->lock();
	for(it_cursos = inscripcionesCursos.begin(); it_cursos != inscripcionesCursos.end(); it_cursos++) {
		clasesCurso = cursos->find(*it_cursos)->second->getClases();
		codCurso = this->Int_toString((*it_cursos));
		for(it_clases = clasesCurso.begin(); it_clases != clasesCurso.end(); it_clases++) {
			Clase *clase = it_clases->second;
			codClase = this->Int_toString(clase->getCodigo());
			std::list<std::string>::iterator iter;
			for(iter = clase->getAdjuntos()->begin();iter != clase->getAdjuntos()->end();++iter) {
				std::string nom_adj = *iter;
				tam_arg = codCurso.size() + 1;
				if(this->enviarCodDescrp(cod, tam_arg, codClase, nom_adj) == -1)
					return -1;
				if(!socket->enviar(codCurso.c_str(), codCurso.size() + 1)) {
					std::cerr << "No se pudo enviar el codigo" << std::endl;
					return -1;
				}
			}
		}
	}
	mutexCurso->unlock();
	return 0;
}

int AtencionUsuario::enviarCartelera() {
	if(this->enviarCursos() == -1)
		return -1;

	if(this->enviarClasesDeCursos() == -1)
		return -1;

	if(this->enviarClasesIndiv() == -1)
		return -1;

	// No envia mas entonces envio un cod de op distinto
	char cod_op = 'F';
	int tam_arg = 0;
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	return 0;
}

int AtencionUsuario::enviarClasesIndiv() {
	// C de clase
	char cod_op = 'C';
	int tam_arg;
	std::string codClase;
	std::string descripcion;

	std::map<unsigned int, Clase*>::iterator it;
	mutexClase->lock();
	for(it = clases->begin(); it != clases->end(); it++) {
		tam_arg = 0;
		codClase = this->Int_toString(it->second->getCodigo());
		descripcion = it->second->getDescripcion();
		if(this->enviarCodDescrp(cod_op, tam_arg, codClase, descripcion) == -1)
			return -1;
	}
	mutexClase->unlock();
	return 0;
}

int AtencionUsuario::enviarCursos() {
	// D de cursos
	char cod_op = 'D';
	int tam_arg;
	std::string descripcion;
	std::string codCurso;

	std::map<unsigned int, Curso*>::iterator it;
	mutexCurso->lock();
	for(it = cursos->begin(); it != cursos->end(); it++) {
		tam_arg = 0;
		codCurso = this->Int_toString(it->second->getCodigo());
		descripcion = it->second->getDescripcion();
		if(this->enviarCodDescrp(cod_op, tam_arg, codCurso, descripcion) == -1)
			return -1;
	}
	mutexCurso->unlock();
	return 0;
}

int AtencionUsuario::enviarClasesDeCursos() {
	// E de clases dentro de cursos
	char cod_op = 'E';
	int tam_arg;
	std::string descripcion;
	std::string codClase;
	std::string codCurso;
	std::map<unsigned int, Curso*>::iterator it_cursos;
	std::map<unsigned int, Clase*>::iterator it_clases;
	std::map<unsigned int, Clase*> clases;
	mutexCurso->lock();
	for(it_cursos = cursos->begin(); it_cursos != cursos->end(); it_cursos++) {
		clases = it_cursos->second->getClases();
		for(it_clases = clases.begin(); it_clases != clases.end(); it_clases++) {
			tam_arg = 0;
			Clase *clase = it_clases->second;
			codClase = this->Int_toString(clase->getCodigo());
			codCurso = this->Int_toString(clase->getCurso());
			descripcion = clase->getDescripcion();
			tam_arg += codCurso.size() + 1;
			if(this->enviarCodDescrp(cod_op, tam_arg, codClase, descripcion) == -1)
				return -1;

			if(!socket->enviar(codCurso.c_str(), codCurso.size() + 1))
				return -1;
		}
	}
	mutexCurso->unlock();
	return 0;
}

int AtencionUsuario::enviarCodDescrp(char cod_op, int tam_arg, std::string codigo, std::string descripcion) {
	if(descripcion.size() > 0)
		tam_arg += codigo.size() + descripcion.size() + 2;
	else
		tam_arg += codigo.size() + 1;

	// Mando la clase
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo enviar el tamaño" << std::endl;
		return -1;
	}
	if(!socket->enviar(codigo.c_str(), codigo.size() + 1)) {
		std::cerr << "No se pudo enviar el codigo" << std::endl;
		return -1;
	}
	if(descripcion.size() > 0) {
		if(!socket->enviar(descripcion.c_str(), descripcion.size() + 1)) {
			std::cerr << "No se pudo enviar la descripcion" << std::endl;
			return -1;
		}
	}
	return 0;
}

void AtencionUsuario::inscripcionClase(char **argumentos) {
	std::string codClase;
	unsigned int cod_Clase = 0;
	codClase = *argumentos;
	cod_Clase = this->String_toInt(codClase);
    if(this->buscarClase(cod_Clase)) {
		if((!this->buscarInscripcion(cod_Clase)) || (this->estadoClase(cod_Clase))) {
			this->inscribirAlumno(cod_Clase, false);
			mutexClase->lock();
			//(*(clases))[cod_Clase]->agregar_socket_chat(socket);
			(*(clases))[cod_Clase]->agregarSocketAlumno_AV(socket_av);
			mutexClase->unlock();
			this->enviarRespuesta('1');
		}
		else {
			//Ya esta incripto a esta clase
			this->enviarRespuesta('2');
		}
	}
	else
		// No existe la clase
		this->enviarRespuesta('0');
}

bool AtencionUsuario::inscripcionCurso(char **argumentos) {
	std::string codCurso;
	unsigned int cod_Curso = 0;
	codCurso = *argumentos;
	cod_Curso = this->String_toInt(codCurso);
    if(this->buscarCurso(cod_Curso)) {
    	mutexCurso->lock();
    	Curso *curso = (*cursos)[cod_Curso];
		if(curso->inscribirAlumno(nombre_usuario, socket_av) == 0) {
			mutexCurso->unlock();
			this->inscribirAlumno(cod_Curso, true);
			this->enviarRespuesta('1');   // Se inscribio con exito
			return true;
		}
		else {
			// Ya esta incripto a este curso
			this->enviarRespuesta('2');
		}
	}
	else
		// No existe el curso
		this->enviarRespuesta('0');
    mutexCurso->unlock();
    return false;
}

void AtencionUsuario::login(bool *profesor, bool *alumno, char cod_op, char **argumentos) {
	bool logeado = false;
	if((!(*alumno)) && (!(*profesor))) {
		nombre_usuario = (*argumentos);
		clave = &((*argumentos)[nombre_usuario.size() + 1]);
		if(!this->verificarInscripcion(nombre_usuario, clave, cod_op))
			this->enviarRespuesta('0'); // no se logueo
		else {
			this->enviarRespuesta('1'); // se logueo
			logeado = true;
		}
	}
	// Si el cod_op es 0 entonces es un instructor
	if(logeado) {
		if(cod_op == '0') {
			*profesor = true;
			*alumno = false;
		}
		// Si el cod_op es 1 entonces es un alumno
		if(cod_op == '1') {
			*alumno = true;
			*profesor = false;
		}
	}
}

void AtencionUsuario::pausarClase() {
	if(codCurso_a_iniciar == 0) {
		mutexClase->lock();
		((*(clases))[codClase_a_iniciar])->pausarClase();
		((*(clases))[codClase_a_iniciar])->join();
		mutexClase->unlock();
		// Si termina una clase el codClase_a_iniciar sera 0
	}
	if(codCurso_a_iniciar > 0) {
		mutexCurso->lock();
		Clase *clase = (*cursos)[codCurso_a_iniciar]->getClase(codClase_a_iniciar);
		clase->pausarClase();
		clase->join();
		mutexCurso->unlock();
	}
}

void AtencionUsuario::continuarClase() {
	if(codCurso_a_iniciar == 0) {
		mutexClase->lock();
		((*(clases))[codClase_a_iniciar])->continuarClase();
		((*(clases))[codClase_a_iniciar])->start();
		mutexClase->unlock();
		std::cout << "Clase reanudada" << std::endl;
	}
	if(codCurso_a_iniciar > 0) {
		mutexCurso->lock();
		Clase *clase = (*cursos)[codCurso_a_iniciar]->getClase(codClase_a_iniciar);
		clase->continuarClase();
		clase->start();
		mutexCurso->unlock();
		std::cout << "Clase de curso reanudada" << std::endl;
	}
}

void AtencionUsuario::terminarClase() {
	bool terminada = false;
	if(codCurso_a_iniciar == 0) {
		mutexClase->lock();
		((*(clases))[codClase_a_iniciar])->terminarClase();
		((*(clases))[codClase_a_iniciar])->join();
		// Si termina una clase el codClase_a_iniciar sera 0
		this->avisar_fin_a_alumnos();
		mutexClase->unlock();
		terminada = true;
	}
	if(codCurso_a_iniciar > 0) {
		mutexCurso->lock();
		Clase *clase = (*cursos)[codCurso_a_iniciar]->getClase(codClase_a_iniciar);
		clase->terminarClase();
		clase->join();
		(*cursos)[codCurso_a_iniciar]->finalizarCurso();
		this->avisar_fin_a_alumnos();
		mutexCurso->unlock();
		terminada = true;
	}
	if(terminada) {
		codCurso_a_iniciar = 0;
		codClase_a_iniciar = 0;
	}
}

bool AtencionUsuario::iniciarClase() {
	if(codCurso_a_iniciar == 0) {
		if(!this->buscarClase(codClase_a_iniciar)) {
			codClase_a_iniciar = 0;
			return false;
		}
		mutexClase->lock();
		if((*(clases))[codClase_a_iniciar]->esta_Terminada()) {
			mutexClase->unlock();
			return false;
		}
		mutexClase->unlock();
		mutexClase->lock();
		// El profesor setea su socket a la clase
		(*(clases))[codClase_a_iniciar]->setSocket_Instr(socket_av);
		// El profesor agrega su socket a la lista de sockets de chat
		//((*(clases))[codClase_a_iniciar])->agregar_socket_chat(socket);
		((*(clases))[codClase_a_iniciar])->iniciarClase();
		((*(clases))[codClase_a_iniciar])->continuarClase();
		((*(clases))[codClase_a_iniciar])->start();
		mutexClase->unlock();
	}
	if(codCurso_a_iniciar > 0) {
	  if(!this->buscarCurso(codCurso_a_iniciar))
	    return false;
	  mutexCurso->lock();
	  if(!(*cursos)[codCurso_a_iniciar]->buscarClase(codCurso_a_iniciar)) {
	    mutexClase->unlock();
	    return false;
	  }
	  (*cursos)[codCurso_a_iniciar]->iniciarClase(codClase_a_iniciar);
	  Clase *clase = (*cursos)[codCurso_a_iniciar]->getClase(codClase_a_iniciar);
	  if(clase->esta_Terminada()) {
	    mutexCurso->unlock();
	    return false;
	  }
	  // El profesor setea su socket a la clase
	  clase->setSocket_Instr(socket_av);
	  // El profesor agrega su socket a la lista de sockets de chat
	 // clase->agregar_socket_chat(socket);
	  clase->continuarClase();
	  clase->iniciarClase();
	 // avisar_inicio_a_alumnos();
	  clase->start();
	  mutexCurso->unlock();
	}
	return true;
}

bool AtencionUsuario::verificarInscripcion(std::string nombre_usuario, std::string clave, char modo) {
	arch_usuarios->clear();
	arch_usuarios->seekg(0, std::ios::beg);
	bool ret = false;
	std::string aux;
	char *linea = new char[100];
	char aux2;
	// Leo nombre de usuario
	while((*arch_usuarios) >> aux) {
		if (aux == nombre_usuario) {
			// Leo la clave
			*arch_usuarios >> aux;
			if (aux == clave) {
				// Leo el modo
				*arch_usuarios >> aux2;
				if(aux2 == '0' || aux2 == modo)
					ret = true;
			}
		}
		arch_usuarios->getline(linea, 100);
	}
	return ret;
}
int AtencionUsuario::avisar_inicio_a_alumnos() {
  // Llama a la funcion avisar_a_alumnos con codigo de operacion 1
  // (inicio de clase)
  return this->avisar_a_alumnos('1');
}
int AtencionUsuario::avisar_fin_a_alumnos() {
  // Llama a la funcion avisar_a_alumnos con codigo de operacion 0
  // (inicio de clase)
  return this->avisar_a_alumnos('0');
}

 // Avisa a los alumnos que la clase ya termino
int AtencionUsuario::avisar_a_alumnos(char cod_op) {
  // Convierte en string el codigo de la clase
	std::string codClase_str = this->Int_toString(codClase_a_iniciar);
	std::string codCurso_str;
	int tam_arg = 0;
	std::list<Socket*>::iterator *it;
	// Si la clase no pertenece a un curso, el codCurso_a_iniciar es 0
	if(codCurso_a_iniciar == 0) {
		*it = (((*(clases))[codClase_a_iniciar])->getSocket_alumnos_av())->begin();
		tam_arg = codClase_str.size() + 1;
		std::cout << (*(*it))->getFd() << std::endl;
		// Envia a todos los alumnos de la clase el codigo de la clase
		for((*it) = (((*(clases))[codClase_a_iniciar])->getSocket_alumnos_av())->begin();
				*it != (((*(clases))[codClase_a_iniciar])->getSocket_alumnos_av())->end(); ++(*it)) {
			std::cout << "(*it)->getFd() " << (*(*it))->getFd() << std::endl;
			if(!(*(*it))->enviar(&cod_op, sizeof(cod_op))) {
				std::cerr << "No se pudo enviar 1" << std::endl;
				return -1;
			}
			if(!(*(*it))->enviar(&tam_arg, sizeof(tam_arg))) {
				std::cerr << "No se pudo enviar 2" << std::endl;
				return -1;
			}
			if(!(*(*it))->enviar(codClase_str.c_str(), codClase_str.size() + 1)) {
				std::cerr << "No se pudo enviar 3" << std::endl;
				return -1;
			}
		}
	} else {
	  // La clase pertenece a un curso, convierte el codigo de curso a iniciar
	  // en string
		codCurso_str = this->Int_toString(codCurso_a_iniciar);
		std::list<Socket*> *list;
		tam_arg = codClase_str.size() + codCurso_str.size() + 2;
		list = (*cursos)[codCurso_a_iniciar]->getClase(codClase_a_iniciar)->getSocket_alumnos_av();
		*it = list->begin();
		// Envia a todos los alumnos de la clase (del curso que corresponda)
		// el codigo de la clase y el codigo del curso
		for((*it) = list->begin();
				*it != list->end(); ++(*it)) {
			if(!(*(*it))->enviar(&cod_op, sizeof(cod_op))) {
				std::cerr << "No se pudo enviar 1" << std::endl;
				return -1;
			}
			if(!(*(*it))->enviar(&tam_arg, sizeof(tam_arg))) {
				std::cerr << "No se pudo enviar 2" << std::endl;
				return -1;
			}
			if(!(*(*it))->enviar(codClase_str.c_str(), codClase_str.size() + 1)) {
				std::cerr << "No se pudo enviar 3" << std::endl;
				return -1;
			}
			if(!(*(*it))->enviar(codCurso_str.c_str(), codCurso_str.size() + 1)) {
				std::cerr << "No se pudo enviar 4" << std::endl;
				return -1;
			}
		}
	}
	return 0;
}

bool AtencionUsuario::CrearCurso(const unsigned int cod_Curso, const std::string descripcion) {
	Curso *curso = NULL;
	if(this->buscarCurso(cod_Curso)) {
		this->enviarRespuesta('0');
		return false;
	}
	curso = new Curso(cod_Curso, descripcion);
	mutexCurso->lock();
	(*(cursos))[cod_Curso] = curso;
	mutexCurso->unlock();
	this->enviarRespuesta('1');
	return true;
}

bool AtencionUsuario::AnadirClase(const unsigned int cod_Curso, const unsigned int cod_Clase,
		const std::string descripcion, const char disponibilidad) {
	Clase *clase = NULL;
	bool fuera_linea = false;
	if(disponibilidad == 'S')
		fuera_linea = true;
	if(!this->buscarCurso(cod_Curso)) {
		// El curso no existe
		this->enviarRespuesta('0');
		return false;
	}
	if((*cursos)[cod_Curso]->buscarClase(cod_Clase)) {
		// La clase ya existe en el curso
		this->enviarRespuesta('2');
		return false;
	}
	clase = new Clase(descripcion, cod_Clase, fuera_linea);
	clase->setCurso(cod_Curso);
	mutexCurso->lock();
	(*cursos)[cod_Curso]->AgregarClase(cod_Clase, clase);
	mutexCurso->unlock();
	// Clase añadida correctamente
	this->enviarRespuesta('1');
	return true;
}

bool AtencionUsuario::CrearClase(const unsigned int cod_Clase, const std::string descripcion,
		const char disponibilidad) {
	Clase *clase = NULL;
	bool fuera_linea = false;
	if(disponibilidad == 'S')
		fuera_linea = true;
	if(this->buscarClase(cod_Clase)) {
		std::cerr << "Clase existente, ponga otro código" << std::endl;
		// Me fijo quienes estan inscriptos en la clase ya creado
		(*(this->clases))[cod_Clase]->verInscriptos();
		// Codigo de respuesta de q no creo la clase
		this->enviarRespuesta('0');
		return false;
	}
	else {
		clase = new Clase(descripcion, cod_Clase, fuera_linea);
		mutexClase->lock();
		(*(this->clases))[cod_Clase] = clase;
		mutexClase->unlock();
		std::cout << "Clase creada: " << descripcion << " " << cod_Clase << std::endl;
	}
	// Codigo de respuesta de creo la clase correctamente
	this->enviarRespuesta('1');
	return true;
}

bool AtencionUsuario::buscarInscripcion(unsigned int codClase) {
	std::list<unsigned int>::iterator it;
	mutexClase->lock();
	for(it = inscripcionesClases.begin(); it != inscripcionesClases.end(); ++it) {
		if((*it) == codClase) {
			mutexClase->unlock();
			return true;
		}
	}
	mutexClase->unlock();
	return false;
}

bool AtencionUsuario::estadoClase(const unsigned int cod_clase) {
	return (*(clases))[cod_clase]->esta_Terminada();
}

// Busca si existe la clave, si existe retorna true
bool AtencionUsuario::buscarClase(const unsigned int codClase) {
	mutexClase->lock();
	if(clases->find(codClase) != clases->end()) {
		mutexClase->unlock();
		return true;
	}
	mutexClase->unlock();
	return false;
}

// Busca si existe la clave, si existe retorna true
bool AtencionUsuario::buscarCurso(const unsigned int codCurso) {
	mutexCurso->lock();
	if(cursos->find(codCurso) != cursos->end()) {
		mutexCurso->unlock();
		return true;
	}
	mutexCurso->unlock();
	return false;
}

// Inscribe un alumno a una clase o a un curso
void AtencionUsuario::inscribirAlumno(const unsigned int codigo, bool curso) {
	if(curso == false) {
		mutexClase->lock();
		this->clases->find(codigo)->second->inscribirAlumno(nombre_usuario);
		inscripcionesClases.push_back(codigo);
		mutexClase->unlock();
	}
	else
		inscripcionesCursos.push_back(codigo);
}

void AtencionUsuario::matarSockets() {
	socket->shutDown();
	delete socket;
	socket_av->shutDown();
	delete socket_av;
}

void AtencionUsuario::pararAtencion() {
	atendiendo = false;
}

void AtencionUsuario::cerrarClase() {
	if(codCurso_a_iniciar == 0) {
		if(codClase_a_iniciar > 0) {
			mutexClase->lock();
			((*clases))[codClase_a_iniciar]->terminarClase();
			((*clases))[codClase_a_iniciar]->join();
			mutexClase->unlock();
		}
	}
	else {
		if(codClase_a_iniciar > 0) {
			mutexCurso->lock();
			Clase *clase = (*cursos)[codCurso_a_iniciar]->getClase(codClase_a_iniciar);
			clase->terminarClase();
			clase->join();
			mutexCurso->unlock();
		}
	}
}

bool AtencionUsuario::enviarRespuesta(char cod1) {
    if(!socket->enviar(&cod1, sizeof(cod1))) {
    	std::cerr << "no se pudo enviar" << std::endl;
    	return false;
    }
    return true;
}

void AtencionUsuario::separar_Argumentos(char **argumentos, unsigned int *cod_Clase, unsigned int *cod_Curso, int tam_arg) {
	std::string codClase;
	std::string codCurso;
	codClase = (*argumentos);
	*cod_Clase = this->String_toInt(codClase);
	tam_arg -= codClase.size();
	if(tam_arg > 1) {   // tengo mas argumentos
		codCurso = (*argumentos)[codClase.size() + 1];
		*cod_Curso = this->String_toInt(codCurso);
	}
}

std::string AtencionUsuario::recibirNomArch(char* argumentos, unsigned int tam_arg, unsigned long tam_arch) {
	std::string nom_arch = argumentos;
	std::cout << "nom arch " << nom_arch << std::endl;
	memcpy(&tam_arch, &argumentos[nom_arch.size() + 1], sizeof(tam_arch));
	std::string codigoClase = &argumentos[nom_arch.size() + 1 + sizeof(tam_arch)];
	std::string codigoCurso = &argumentos[nom_arch.size() + 1 +
	          sizeof(tam_arch) + codigoClase.size() + 1];
	std::cout << " codigo clase: " << codigoClase << std::endl;
	std::cout << " codigo curso: " << codigoCurso << std::endl;
	if(codigoCurso == "0"){
		mutexClase->lock();
		this->clases->find(atoi(codigoClase.c_str()))->
			second->agregarArchivo(nom_arch);
		mutexClase->unlock();
	}
	else {
		mutexClase->lock();
		this->cursos->find(String_toInt(codigoCurso))->second->
			getClase(String_toInt(codigoClase))->agregarArchivo(nom_arch);
		mutexClase->unlock();
	}
	return nom_arch;
}


unsigned int AtencionUsuario::String_toInt(std::string str) {
	unsigned int entero = 0;
	std::stringstream ss;
	ss << str;
	ss >> entero;
	return entero;
}

std::string AtencionUsuario::Int_toString(unsigned int entero) {
	std::string str;
	std::stringstream ss;
	ss << entero;
	ss >> str;
	return str;
}

AtencionUsuario::~AtencionUsuario() {}
